iT邦幫忙

2022 iThome 鐵人賽

DAY 10
0
Modern Web

就是要搞懂 JavaScript 啦!系列 第 10

Day10 我也有自己的地盤啦!──區塊作用域

  • 分享至 

  • xImage
  •  

區塊作用域(Block Scope)

在 JS 最早的版本中,只有函式能夠創造自己的作用域。

接著 ES3 新增了 try/catch ,其中的 catch 擁有自己的作用域。

try {
    // do something
}
catch (err) {
    console.log( err );
}

console.log( err ); // ReferenceError: `err` not found

來到 ES6 時,JS 新增了 letconst 這兩個宣告變數的關鍵字,開始能夠使用「區塊(Block)」也就是 {} 內部的範圍來劃定作用域。

能劃定區塊作用域的關鍵字:

  • let :宣告可重複修改的變數。
  • const:宣告常數(constant),也就是無法修改的變數。宣告時就必須賦值,之後只能讀取(RHS),不可修改其值(LHS)。

let

// 使用 var
for (var i = 0; i < 10; i++) {
	console.log( i );
}
console.log( `當前 i 等於 ${i}` ); 

// 使用 let
for (let j = 0; j < 10; j++) {
	console.log( j );
}
console.log( `當前 j 等於 ${j}` );
// ReferenceError: j is not defined

避免重複宣告

var 不同的其中一點是,let 禁止重複宣告,比如以下程式碼是合法的:

var a = "Hi!";
console.log(a); // Hi!

var a = "Hello!";
console.log(a); // Hello!

但這樣就出錯了:

let a = "Hi!";
console.log(a); // Hi!

let a = "Hello!";
console.log(a);
// Uncaught SyntaxError: Identifier 'a' has already been declared

const

const 的特性是只能在宣告時賦值,且之後無法改變其值。

只能在宣告時賦值:

const a;
a = "JavaScript";
// SyntaxError: Missing initializer in const declaration

宣告後無法改變其值:

const a = "Java";
a = "JavaScript";
// TypeError: Assignment to constant variable.

以下來做個 varletconst 的比較:

var foo = true;

if (foo) {
	var a = 1;
	let b = 2; // 屬於 if 的區塊作用域 
	const c = 3; // 屬於 if 的區塊作用域

	a = 2; // OK!
	b = 3; // OK!
	c = 4; // 重複賦值而報錯──TypeError: Assignment to constant variable.
}

console.log( a ); // 2
console.log( b ); // ReferenceError!
console.log( c ); // ReferenceError!

區塊作用域的優點

垃圾回收(Garbage Collection)

使用區塊作用域可明確告知引擎,執行完區塊後可清空整個區塊的資料。

function process(data) {
	// do something
}

{
	let someReallyBigData = { ... };
	process( someReallyBigData );
	// someReallyBigData 存在到這裡為止
}
// someReallyBigData 已被回收,從記憶體消失
console.log(someReallyBigData) // ReferenceError

let 循環

迭代的變數被鎖在區塊作用域內部,不會汙染全域作用域。

範例一

for (let i = 0; i < 10; i++) {
	console.log( i );
}
console.log( i ); // ReferenceError

範例二

{
	let j;
	for (j = 0; j < 10; j++) {
		let i = j + 1; // 每次迭代都重新绑定
		console.log( i, j );
	}
	console.log( i ); // ReferenceError
}
console.log( j ); // ReferenceError

填補區塊作用域

填補(Polyfill)

所謂的填補,是指使用新版本撰寫的代碼,需要在舊環境運行的狀況下,由於舊版本不支援新版本某些功能,於是使用舊版本擁有的技術實現新版本功能,以「填補」該功能的缺失。

Babel 就是一個有名的 JS 編譯器,另外 Google 也開發並維護了一個以 Node.js 開發的,名為 Traceur 的專案,以上兩者都可以將 ES6 以上的功能轉換為舊版可運行的代碼。

以下就舉例一些在舊版本達成區塊作用域效果的方法:

使用 try/catch(ES3+)

範例一
a 是屬於全域變數,而非 if 區塊自己的變數

if (true) {
  var a = 1;
  console.log(a) // 1
}
console.log(a) // 1

使用 try/catch 改寫

try{
  throw 2
}catch(a){
  console.log( a ); // 2
  var b = "2";
  console.log( b ); // "2"
}
console.log( b ); // undefined
console.log( a ); // ReferenceError

從上面範例可以看出,catch 的作用域和函式作用域的不同點在於:函式作用域的提升僅提升到函式內部最上面,而 catch 作用域使用 var 宣告的變數,會提升到外部區塊。

使用 IIFE

(function foo(){
	var a = 1;
	console.log( a ); // 1
})();

console.log( a ); // ReferenceError

參考資料


上一篇
Day09 這是我的地盤──函式作用域
下一篇
Day11 咱們井水不犯河水──作用域的優點
系列文
就是要搞懂 JavaScript 啦!73
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言